package com.jasony.tools.excel;

import com.jasony.tools.excel.typehandler.TypeHandler;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;

public abstract class ExcelSheet<T> {
    private String name;
    private Collection<T> data;

    public Class<T> getClazz() {
        return clazz;
    }

    public void setClazz(Class<T> clazz) {
        this.clazz = clazz;
    }

    private Class<T> clazz;

    public ExcelSheet(String name, Collection<T> data, Class<T> clazz) {
        this.name = name;
        this.data = data;
        this.clazz = clazz;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Collection<T> getData() {
        return data;
    }

    public void setData(Collection<T> data) {
        this.data = data;
    }

    public void writeWorkBook(Workbook workbook) {
        List<ExcelColumnAttr> columns = analysisColumns(workbook, this.getClazz());
        int maxRowCount = 1048574;
        if (workbook instanceof HSSFWorkbook) {
            maxRowCount = 65534;
        }
        fullSheetData(workbook, this, columns, maxRowCount);
    }

    private <T> List<ExcelColumnAttr> analysisColumns(Workbook workbook, Class<T> clazz) {
        List<ExcelColumnAttr> columnAttrs = new ArrayList<>();
        Method[] methods = clazz.getDeclaredMethods();
        DataFormat dataFormat = workbook.createDataFormat();
        if (methods != null && methods.length != 0) {
            for (Method method : methods) {
                if (method.getName().startsWith("get") && method.isAnnotationPresent(ExcelColumn.class)) {
                    ExcelColumn excelColumn = method.getAnnotation(ExcelColumn.class);
                    ExcelColumnAttr column = new ExcelColumnAttr(method);
                    column.setTitle(excelColumn.title());
                    column.setPriority(excelColumn.priority());
                    column.setDefaultValue(excelColumn.defaultValue());
                    CellStyle cellStyle = workbook.createCellStyle();
                    column.setCellStyle(cellStyle);
                    if (StringUtils.isEmpty(excelColumn.format())) {
                        cellStyle.setDataFormat(dataFormat.getFormat("General"));
                    } else {
                        cellStyle.setDataFormat(dataFormat.getFormat(excelColumn.format()));
                    }
                    columnAttrs.add(column);
                }
            }
        }
        columnAttrs.sort((o1, o2) -> {
            if (o1.getPriority().equals(o2.getPriority())) {
                return 0;
            }
            return o1.getPriority() > o2.getPriority() ? 1 : -1;
        });
        return columnAttrs;
    }

    private static void fullSheetData(Workbook workbook, ExcelSheet excelSheet, List<ExcelColumnAttr> columns, int maxRowCount) {
        Collection source = excelSheet.getData();

        int sheetIndex = 0;
        int rowIndex = 0;
        Sheet sheet = null;
        if (source == null || source.isEmpty() || columns == null || columns.isEmpty()) {
            sheet = workbook.createSheet(excelSheet.getName() + (sheetIndex++ > 0 ? "_" + String.valueOf(sheetIndex) : ""));
            fullSheetHeader(sheet, columns);
            return;
        }
        for (Object data : source) {
            if (rowIndex >= maxRowCount || rowIndex == 0) {
                sheet = workbook.createSheet(excelSheet.getName() + (sheetIndex++ > 0 ? "_" + String.valueOf(sheetIndex) : ""));
                fullSheetHeader(sheet, columns);
                rowIndex = sheet.getPhysicalNumberOfRows();
            }
            if (Objects.isNull(data)) {
                continue;
            }
            Row row = sheet.createRow(rowIndex++);
            for (int i = 0; i < columns.size(); i++) {
                ExcelColumnAttr column = columns.get(i);
                try {
                    Cell cell = row.createCell(i);
                    cell.setCellStyle(column.getCellStyle());
                    Object value = column.getMethod().invoke(data);
                    Class<?> returnType = column.getMethod().getReturnType();
                    if (Objects.isNull(value)) {
                        cell.setCellValue(column.getDefaultValue());
                    } else {
                        TypeHandler typeHandler = ExcelProperties.getTypeHandlers().getOrDefault(returnType, ExcelProperties.getTypeHandlers().get(String.class));
                        typeHandler.write(value, cell);
                    }
                } catch (IllegalAccessException | InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    private static void fullSheetHeader(Sheet sheet, List<ExcelColumnAttr> columns) {
        Row headRow = sheet.createRow(0);
        for (int i = 0; i < columns.size(); i++) {
            ExcelColumnAttr column = columns.get(i);
            String title = column.getTitle();
            if (StringUtils.isEmpty(title)) {
                title = column.getDataAttributes().replace("get", "");
                if (Character.isLowerCase(title.charAt(0))) {
                    title = new StringBuilder(Character.toUpperCase(title.charAt(0)))
                            .append(title.substring(1)).toString();
                }
            }
            headRow.createCell(i).setCellValue(title);
        }
    }
}
